package com.sakiko.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

// todo: aop_05
@Aspect
@Component
public class LogAspect03 {

    // long startTime;  切面 aop里不写全局变量
//    ThreadLocal<Long> longThreadLocal = new ThreadLocal<>();

    @Before("com.sakiko.pointcuts.MyPointcut.pointcut05()")
    public void before() {
        long startTime = System.currentTimeMillis();
//        longThreadLocal.set(startTime);
    }

    @After("com.sakiko.pointcuts.MyPointcut.pointcut05()")
    public void after(JoinPoint joinPoint) {
        long endTime = System.currentTimeMillis();
        String className = joinPoint.getClass().getSimpleName();
        String methodName = joinPoint.getSignature().getName();
        /* note: 想使用两个通知方法进行数据共享！！ 不方便
            解决 1、提取为全局变量 【不行，通知方法会被很多目标方法调用，没法保证参数是上一次生成的】
                2、线程本地变量 ThreadLocal 【共享域，范围一个线程共享】
                3、使用环绕通知 【此情况下，推荐】
          */
//        Long startTime = longThreadLocal.get();
    }

    // 使用 @Around注解标明环绕通知方法
    @Around("com.sakiko.pointcuts.MyPointcut.pointcut05()")
    public Object manageTransaction(
            // 通过在通知方法形参位置声明 ProceedingJoinPoint类型的形参，
            // Spring会将这个类型的对象传给我们
            ProceedingJoinPoint joinPoint) {

        // 通过ProceedingJoinPoint对象获取外界调用目标方法时传入的实参数组
        Object[] args = joinPoint.getArgs();

        // 声明变量用来存储目标方法的返回值
        Object targetMethodReturnValue = null;

        try {
            // 前置通知
            long startTime = System.currentTimeMillis();

            // 过ProceedingJoinPoint对象调用目标方法
            // 目标方法的返回值一定要返回给外界调用者
            targetMethodReturnValue = joinPoint.proceed(args);

            // 返回通知
            long endTime = System.currentTimeMillis();
            System.out.println(joinPoint.getTarget().getClass().getSimpleName() + ":" + joinPoint.getSignature().getName()
                    + " 方法消耗时间:" + (endTime - startTime));
        } catch (Throwable e) {
            // 异常通知
            System.out.println(e.getMessage());
        } finally {
            // 后置通知
            System.out.println("后置通知...");
        }

        return targetMethodReturnValue;
    }
}
